﻿using System;
using System.Collections.Generic;
using System.Text;

using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;


namespace WebCrawler
{
	/// <summary>
	/// Веб-паук
	/// </summary>
	public class Crawler : ICrawler
	{
		List<UriPair> linksToVisit = new List<UriPair>();  // list of links to visit
		List<string> visitedUris   = new List<string>();   // list of links visited

		string startPointAdress = "http://maximals.ru";

		Thread thdMain = null;

		public ThreadState ThreadState
		{
			get
			{
				return thdMain.ThreadState;
			}
		}

		public Crawler(string startAdress) //start point of crawler  
		{
			startPointAdress = startAdress;
		}


		public void Run()
		{
			if (PageVisited != null)
			{
				if (thdMain == null)
				{
					thdMain = new Thread(new ThreadStart(ThreadProc));
					thdMain.Start();
				}
			}
		}

		protected void ThreadProc()
		{
			Uri startPoint = new Uri(startPointAdress);
			linksToVisit.Add(new UriPair(startPoint, string.Empty));

			while (linksToVisit.Count > 0)
			{
				RequestSite(linksToVisit[0]);
				linksToVisit.RemoveAt(0);
				Thread.Sleep(1);
			}
		}

		public void Stop()
		{
			if (thdMain != null)
				if (thdMain.ThreadState != ThreadState.Stopped)
				{
					thdMain.Abort();
					thdMain = null;
				}
		}


		protected void RequestSite(UriPair uriPair)
		{
			try
			{
				// Отсекаем некоторые протоколы, чтобы можно было создать запрос
				if (!uriPair.RelativeUri.StartsWith("mailto:", StringComparison.InvariantCultureIgnoreCase)   &&
					!uriPair.RelativeUri.StartsWith("skype:", StringComparison.InvariantCultureIgnoreCase)    &&
					!uriPair.RelativeUri.StartsWith("javascript:", StringComparison.InvariantCultureIgnoreCase))
				{
					WebRequest req = WebRequest.Create(new Uri(uriPair.BaseUri, uriPair.RelativeUri));
					req.Timeout = 10000;
					int i = 0;
					if (req.RequestUri.AbsoluteUri.Contains("/loc"))
						i = 1;/**/

					if (!visitedUris.Contains(req.RequestUri.AbsoluteUri))
					{
						try
						{
							WebResponse res = req.GetResponse();
							if (res.ContentType.Split('/')[0] == "text")
							{
								Stream st = res.GetResponseStream();
								StreamReader rdr = new StreamReader(st);
								string s = rdr.ReadToEnd();

								OnPageVisited(new PageEventArgs(res, s));
								visitedUris.Add(res.ResponseUri.AbsoluteUri);

								linksToVisit.AddRange(GetLinks(res.ResponseUri, s));
							}
						}
						catch (Exception ex)
						{
							Log.Write(
								uriPair.BaseUri.AbsoluteUri + " -> " + uriPair.RelativeUri + Environment.NewLine + Environment.NewLine +
								ex.Message + Environment.NewLine + Environment.NewLine +
								ex.StackTrace
								);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Log.Write(
						uriPair.BaseUri.AbsoluteUri + " -> " + uriPair.RelativeUri + Environment.NewLine + Environment.NewLine +
						ex.Message + Environment.NewLine + Environment.NewLine +
						ex.StackTrace
					);
			}
		}


		protected List<UriPair> GetLinks(Uri baseUri, string htmlPage)
		{
			List<UriPair> results = new List<UriPair>();

			Match m;
			string hrefPattern = "href\\s*=\\s*(?:\"(?<href>[^\"]*)\"|(?<href>\\S+))";

			m = Regex.Match(htmlPage, hrefPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
			while (m.Success)
			{
				results.Add(new UriPair(baseUri, m.Groups["href"].ToString()));
				m = m.NextMatch();
			}

			return results;
		}



		public delegate void PageEventHandler(object sender, PageEventArgs e);
		public event PageEventHandler PageVisited;
		protected virtual void OnPageVisited(PageEventArgs e)
		{
			if (PageVisited != null)
			{
				PageVisited(this, e);
			}
		}
	}

}
